#include <my_header.h>

// 设置文件类型和权限字符串
void set_type_mode(mode_t mode, char *tm_str){
    // 处理第一个字符，即文件类型
    switch(mode & S_IFMT){
    case S_IFBLK: tm_str[0] = 'b'; break;
    case S_IFCHR: tm_str[0] = 'c'; break;
    case S_IFDIR: tm_str[0] = 'd'; break;
    case S_IFIFO: tm_str[0] = 'p'; break;
    case S_IFLNK: tm_str[0] = 'l'; break;
    case S_IFREG: tm_str[0] = '-'; break;
    case S_IFSOCK: tm_str[0] = 's'; break;
    default:    tm_str[0] = '?'; break;
    }

    //处理后续字符
    //设置拥有者的权限
    tm_str[1] = (mode & 0400) ? 'r' : '-';
    tm_str[2] = (mode & 0200) ? 'w' : '-';
    tm_str[3] = (mode & 0100) ? 'x' : '-';
    //设置拥有者组权限
    tm_str[4] = (mode & 0040) ? 'r' : '-';
    tm_str[5] = (mode & 0020) ? 'w' : '-';
    tm_str[6] = (mode & 0010) ? 'x' : '-';
    
    tm_str[7] = (mode & 0004) ? 'r' : '-';
    tm_str[8] = (mode & 0002) ? 'w' : '-';
    tm_str[9] = (mode & 0001) ? 'x' : '-';
    tm_str[10] = '\0';
}

// 获取格式化的时间字符串
void set_time(time_t mtime, char *time_str){
    // 由于tm结构体中存储的是月份的整数值，我们需要的是月份字符串，所以用一个字符串数组来存储月份字符串
    const char month_arr[][10] = {
        "1月", "2月", "3月", "4月", "5月", "6月",
        "7月", "8月", "9月", "10月", "11月", "12月"
    };  // tm结构体当中的月份范围是[0, 11]，刚好可以适配这个数组

    // 调用localtime函数，获取tm结构体指针
    struct tm* st_tm = localtime(&mtime);
    // 构建时间字符串,格式为：月份 天数 时：分
    sprintf(time_str, "%s %2d %02d:%02d",
            month_arr[st_tm->tm_mon],
            st_tm->tm_mday,
            st_tm->tm_hour,
            st_tm->tm_min);
}

/* Usage: ./07_myls pathname 或 ./07_myls */
int main(int argc, char* argv[]) {
    char* dir_name; // 存储目录名的指针
    if(argc == 1) {
        dir_name = ".";  // 如果命令行参数没有提供要打印的目录，就打印当前工作目录
    }
    else if (argc == 2){
        dir_name = argv[1]; // 否则使用提供的命令行参数作为待打印的目标
    }else {
        fprintf(stderr, "args num error!\n");
        exit(1);
    }

    DIR* dirp = opendir(dir_name); // 打开指定的目录
    ERROR_CHECK(dirp, NULL, "opendir");

    // 改变工作目录到指定目录
    int ret = chdir(dir_name);
    ERROR_CHECK(ret, -1, "chdir");

    struct dirent* pdirent;
    // 遍历目录项
    while ((pdirent = readdir(dirp)) != NULL) {
        struct stat stat_buf;
        // 获取目录项的详细信息
        int ret = stat(pdirent->d_name, &stat_buf);
        ERROR_CHECK(ret, -1, "stat");

        char mode_str[1024] = { 0 }; // 保存文件类型和权限信息
        set_type_mode(stat_buf.st_mode, mode_str); // 设置类型和权限

        char time_str[1024] = { 0 }; // 保存格式化后的时间信息
        /*
            localtime需要传入时间戳描述
            所以这里传参时可以写stat_buf.st_mtime（宏定义，更简写）
            也可以写stat_buf.st_mtim.tv_sec（宏定义的原版代码）
            但不能直接写stat_buf.st_mtim
            原因上面已经讲过了！
        */
        set_time(stat_buf.st_mtime, time_str); // 获取时间字符串

        printf("%s %2lu %s %s %6lu %s %s\n",
               mode_str,                            // 文件类型与权限
               stat_buf.st_nlink,                   // 硬链接数，不足2个字符的在前面补空格
               getpwuid(stat_buf.st_uid)->pw_name,  // 拥有者名
               getgrgid(stat_buf.st_gid)->gr_name,  // 拥有者组名
               stat_buf.st_size,                    // 文件大小,使用%4lu表示最少输出4个字符，若不足4个字符则在前面补空格
               time_str,                            // 最后修改时间字符串
               pdirent->d_name);                    // 文件名
    }

    // 关闭目录
    closedir(dirp);
    return 0;
}
